// -*-mode:c++; coding:utf-8-*-

#ifndef _COMPRESSOR_MINILZO_HPP_
#define _COMPRESSOR_MINILZO_HPP_

#include <unistd.h>

#include "wrapper.hpp"

namespace {
#include "minilzo/minilzo.h"
}

namespace xbase
{
	namespace compressor
	{
		struct minilzo
		{
			// return - zlen if success, or nagetive number
			template<typename Buffer>
			static inline
			ssize_t compress(Buffer &buffer,
					      const boost::asio::const_buffer &data) {
				assert(lzo_init() == LZO_E_OK); // 必须检查是否满足minilzo的计算环境

				const uint32_t &in_size =
					boost::asio::buffer_size(data);
				const std::size_t max_out_size = (in_size + (in_size >> 4) + 67
								  + sizeof(uint32_t) /*for header length*/);
				const std::size_t workmem_size = LZO1X_MEM_COMPRESS;
				boost::asio::mutable_buffer out = buffer.prepare(max_out_size + workmem_size);
				lzo_voidp workmem = boost::asio::buffer_cast<char*>(out) + max_out_size;
				integer_wrapper<uint32_t, kb::network_byteorder> *in_size_wrapper = reinterpret_cast<integer_wrapper<uint32_t, kb::network_byteorder> *>(boost::asio::buffer_cast<char*>(out));
				in_size_wrapper->set(in_size);
				out = out + in_size_wrapper->bytes();

				lzo_uint real_out_size = boost::asio::buffer_size(out);
				const int ret = lzo1x_1_compress(boost::asio::buffer_cast<const lzo_bytep>(data),
								 in_size,
								 boost::asio::buffer_cast<lzo_bytep>(out),
								 &real_out_size,
								 workmem);
				if(ret != LZO_E_OK)
				{
					return -1;
				}

				real_out_size += in_size_wrapper->bytes();
				buffer.commit(real_out_size);
				return real_out_size;
			}

			// return - zlen if success, or nagetive number
			template<typename Buffer>
			static inline
			ssize_t decompress(Buffer &buffer,
						const boost::asio::const_buffer &data) {
				assert(lzo_init() == LZO_E_OK); // 必须检查是否满足minilzo的计算环境
				
				if(boost::asio::buffer_size(data) < sizeof(uint32_t))
				{
					return -1;
				}
				
				const integer_wrapper<uint32_t, kb::network_byteorder> *expect_out_size_wrapper = reinterpret_cast<const integer_wrapper<uint32_t, kb::network_byteorder> *>(boost::asio::buffer_cast<const char *>(data));
				const uint32_t out_size = expect_out_size_wrapper->get();
				boost::asio::mutable_buffer out = buffer.prepare(out_size);

				const boost::asio::const_buffer real_data =
					data + expect_out_size_wrapper->bytes();
				lzo_uint real_out_size = boost::asio::buffer_size(out);
				const int ret = lzo1x_decompress(boost::asio::buffer_cast<const lzo_bytep>(real_data),
								 boost::asio::buffer_size(real_data),
								 boost::asio::buffer_cast<lzo_bytep>(out),
								 &real_out_size,
								 NULL);
				if(ret != LZO_E_OK)
				{
					return -1;
				}

				assert(out_size == real_out_size);
				buffer.commit(real_out_size);
				return real_out_size;
			}
		};

		// return - zlen if success, or nagetive number
		//          -1 for input data error;
		//          -2 for output buffer too small
		template<>
		inline
		ssize_t minilzo::decompress(const boost::asio::mutable_buffer &buffer,
						 const boost::asio::const_buffer &data)
		{
			assert(lzo_init() == LZO_E_OK); // 必须检查是否满足minilzo的计算环境
			
			if(boost::asio::buffer_size(data) < sizeof(uint32_t))
			{
				return -1;
			}

			const integer_wrapper<uint32_t, kb::network_byteorder> *expect_out_size_wrapper = reinterpret_cast<const integer_wrapper<uint32_t, kb::network_byteorder> *>(boost::asio::buffer_cast<const char *>(data));

			const uint32_t out_size = expect_out_size_wrapper->get();
			if(boost::asio::buffer_size(buffer) < out_size) // not enough
			{
				return -1;
			}

			const boost::asio::const_buffer real_data =
				data + expect_out_size_wrapper->bytes();
			lzo_uint real_out_size = boost::asio::buffer_size(buffer);
			lzo_bytep dest = boost::asio::buffer_cast<lzo_bytep>(buffer);
			const lzo_bytep src = boost::asio::buffer_cast<const lzo_bytep>(real_data);
			const int ret = lzo1x_decompress(src,
							 boost::asio::buffer_size(real_data),
							 dest,
							 &real_out_size,
							 NULL);
			if(ret != LZO_E_OK)
			{
				return -1;
			}
			assert(out_size == real_out_size);
			return real_out_size;
		}
	} // namespace compressor

} // namespace xbase

#endif	// _COMPRESSOR_MINILZO_HPP_
